#include "daemon.h"

typedef struct
{
    uint16_t pid;
    string process_name;
} RuningProcess;

vector<RuningProcess> RangeProcess();

vector<string> StripList(const char *in, const char *d)∫∫
{
    vector<string> ret;
    map<uint16_t,bool> pos;
    vector<uint16_t> pos_strip_continue;
    int lastpos = -1;

    if ((nullptr == in) || (nullptr == d))
    {
        return ret;
    }
    int len = strlen(in);
    int sublen = strlen(d);
    if (len <= 0)
        return ret;
    if (sublen <= 0)
        return ret;

    for (int i = 0; i < (len - sublen + 1); i++)
    {
        bool found = true;
        for (int j = 0; j < strlen(d); j++)
        {
            if (in[i + j] != d[j])
            {
                found = false;
            }
        }
        if (!found)
        {
            pos.insert(make_pair(i,true));
            i += sublen - 1;
            continue;
        }
    }
    if (pos.size() == 0)
    {
        ret.push_back(string(in));
        return ret;
    }
    bool started = true;
    uint16_t startpos = 0;  
    uint16_t endpos = 0;

    for(int i = 0;i < len - 1;i++){

        if(pos[i] == true){
            if(started){
                startpos = i;
                started = false;
            }
            if((pos[i + sublen] != true)){
                endpos = i;
                // std::cout<<startpos<<" "<<endpos<<"\r\n";
                ret.push_back(string(&in[startpos],&in[endpos + 1]));
                started = true;
            }
        }
        if(i == (len - 2)){
            i = len - 1;
            if(pos[i] == true){
                if(started){
                    startpos = i;
                    endpos = i;
                    // std::cout<<startpos<<" "<<endpos<<"\r\n";
                    ret.push_back(string(&in[startpos],&in[endpos + 1]));
                }else{
                    endpos = i;
                    // std::cout<<startpos<<" "<<endpos<<"\r\n";
                    ret.push_back(string(&in[startpos],&in[endpos + 1]));
                }
            }
  
        }
    }

    return ret;
}


#ifdef linux

inline bool existed(const std::string &name)
{
    return (access(name.c_str(), F_OK) != -1);
}

void _process_start(string path)
{

    auto file = popen(path.c_str(), "r");

    if (file < 0)
    {

        fprintf(stderr, "execl failed:%s", strerror(errno));

        return;
    }

    pclose(file);
}

vector<RuningProcess> RangeProcess()
{

    FILE *pstr;

    char cmd[128], buff[512], *p;

    vector<RuningProcess> ret;

    int iPID;

    int pidPosition = 1;

    int pInfoPosition = 7;

    memset(cmd, 0, sizeof(cmd));

    sprintf(cmd, "ps ");

    pstr = popen(cmd, "r");

    if (pstr == NULL)
    {

        return ret;
    }

    memset(buff, 0, sizeof(buff));

    bool first = true;

    while (1)
    {

        RuningProcess ins;

        fgets(buff, 512, pstr);

        if (first)
        {

            first = false;

            continue;
        }

        if (feof(pstr))

        {

            break;
        }

        auto trip = StripList(buff, " ");

        ins.pid = atoi(trip[0].c_str());

        ins.process_name = trip[3];

        ret.push_back(ins);
    }

    pclose(pstr);

    return ret;
}

#endif

#ifdef _WIN32

static BOOL KillProcess(DWORD dwPid)

{
    HANDLE hPrc;
    if (0 == dwPid)
        return FALSE;
    hPrc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); // Opens handle to the process.
    if (!TerminateProcess(hPrc, 0)) // Terminates a process.
    {

        CloseHandle(hPrc);

        return FALSE;
    }
    else
        WaitForSingleObject(hPrc, 2000); // At most ,waite 2000  millisecond.
    CloseHandle(hPrc);
    return TRUE;
}

static BOOL KillProcessByName(const TCHAR *lpszProcessName)
{
    unsigned int pid = -1;
    BOOL retval = TRUE;
    if (lpszProcessName == NULL)
        return -1;

    DWORD dwRet = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(PROCESSENTRY32);
    int flag = Process32First(hSnapshot, &processInfo);
    // Find the process with name as same as lpszProcessName

    while (flag != 0)
    {
        printf("kill process find %s\r\n", processInfo.szExeFile);
        if (strcmp(processInfo.szExeFile, lpszProcessName) == 0)
        {
            // Terminate the process.
            pid = processInfo.th32ProcessID;
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
            printf("kill process pid %d\r\n", pid);
            if (TerminateProcess(hProcess, 0) != TRUE)
            { // Failed to terminate it.
                retval = FALSE;
                break;
            }
        }
        flag = Process32Next(hSnapshot, &processInfo);
    } // while (flag != 0)
    CloseHandle(hSnapshot);
    if (pid == -1)
        return FALSE;
    return retval;
}

static BOOL SetProcessPrivilege(char *lpName, BOOL opt)

{
    HANDLE tokenhandle;
    TOKEN_PRIVILEGES NewState;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle))
    {
        LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid);
        NewState.PrivilegeCount = 1;
        NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0;
        AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL);
        CloseHandle(tokenhandle);
        return 1;
    }
    else

    {
        return 0;
    }
}

// static int RangeProcess()
// {
//     DWORD Proc_pid[1024], Retn_bytes, Proc_count, Retn_bytes2;

//     unsigned int i;

//     HMODULE hMod[1024];

//     HANDLE hProcess;

//     char szModName[MAX_PATH];

//     if (EnumProcesses(Proc_pid, sizeof(Proc_pid), &Retn_bytes))

//     {

//         Proc_count = Retn_bytes / sizeof(DWORD);

//         SetProcessPrivilege("SeDebugPrivilege", 1);

//         for (i = 0; i < Proc_count; i++)

//         {

//             hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Proc_pid[i]);

//             if (hProcess != NULL)

//             {

//                 EnumProcessModules(hProcess, hMod, sizeof(hMod), &Retn_bytes2);

//                 GetModuleFileNameEx(hProcess, hMod[0], szModName, sizeof(szModName));

//                 // printf("PID=%d Path=%s\n", Proc_pid[i], szModName);
//             }

//             CloseHandle(hProcess);
//         }

//         SetProcessPrivilege("SeDebugPrivilege", 0);
//     }

//     return 0;
// }

static int test_fork()
{
    char szCommandLine[] = "D:\\game\\The Legend of Zelda Breath of the Wild\\cemu\\cemu.exe";
    STARTUPINFO si = {sizeof(si)};
    PROCESS_INFORMATION pi;
    si.dwFlags = STARTF_USESHOWWINDOW; //指定wShowWindow成员有效
    si.wShowWindow = TRUE; //此成员设为TRUE的话则显示新建进程的主窗
    BOOL bRet = CreateProcess(
        NULL, //不在此指定可执行文件的文件名
        szCommandLine, //命令行参
        NULL, //默认进程安全
        NULL, //默认进程安全
        FALSE, //指定当前进程内句柄不可以被子进程继承
        CREATE_NEW_CONSOLE, //为新进程创建一个新的控制台窗口
        NULL, //使用本进程的环境变量
        NULL, //使用本进程的驱动器和目录
        &si,
        &pi);
    if (bRet)
    {

        //不使用的句柄最好关
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        printf("new process id  %d\n", pi.dwProcessId);
        printf("new thread id %d\n", pi.dwThreadId);
    }
    return 0;
}

#endif

DaemonizeMonitor::DaemonizeMonitor(string path)
{
#ifdef linux
    auto process = RangeProcess();
    for (auto itr = process.begin(); itr != process.end(); itr++)
    {
        std::cout << itr->process_name << std::endl;
        m_pids[itr->process_name.substr(0, itr->process_name.size() - 1)] = itr->pid;
    }
#endif
    auto strip = StripList(path.c_str(), "/");
    if(strip.size() > 0){
        std::cout<< strip[strip.size() - 1]<<std::endl;
        if(m_running_pid[strip[strip.size() - 1]] > 0){
        }else{
            this->AddNewProcess(path);
        }
    }

}

#ifdef linux
void start_process(string path)
{

    std::thread p(_process_start, path);

    p.detach();
}
#endif

int DaemonizeMonitor::AddNewProcess(string path)
{
    if (path == "")
    {
        return -1;
    }
    this->m_path_process.push_back(path);
#ifdef linux
    if (!existed(path))
    {
        return -1;
    }
    start_process(path);
    usleep(100000);
    auto strip = StripList(path.c_str(), "/");
    
    auto process = RangeProcess();
    for (auto itr = process.begin(); itr != process.end(); itr++)
    {
        m_pids[itr->process_name.substr(0, itr->process_name.size() - 1)] = itr->pid;
    }
    if (m_pids[strip[strip.size() - 1]]  > 0)
        this->m_running_pid[strip[strip.size() - 1]] = m_pids[strip[strip.size() - 1]];
#endif
#ifdef _WIN32
    STARTUPINFO si = {sizeof(si)};
    PROCESS_INFORMATION pi;
    si.dwFlags = STARTF_USESHOWWINDOW; //指定wShowWindow成员有效
    si.wShowWindow = TRUE; //此成员设为TRUE的话则显示新建进程的主窗

    BOOL bRet = CreateProcess(
        NULL, //不在此指定可执行文件的文件名
        (LPSTR)path.c_str(), //命令行参
        NULL, //默认进程安全
        NULL, //默认进程安全
        FALSE, //指定当前进程内句柄不可以被子进程继承
        CREATE_NEW_CONSOLE, //为新进程创建一个新的控制台窗口
        NULL, //使用本进程的环境变量
        NULL, //使用本进程的驱动器和目录
        &si,
        &pi);

    if (bRet)
    {
        //不使用的句柄最好关
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        printf("new process id  %d\n", pi.dwProcessId);
        this->m_running_pid[path] = pi.dwProcessId;
        return 0;
    }
    return -1;
#endif
}

int DaemonizeMonitor::StartMonitor()
{

#ifdef _WIN32
    DWORD Proc_pid[1024], Retn_bytes, Proc_count, Retn_bytes2;
    unsigned int i;
    HMODULE hMod[1024];
    HANDLE hProcess;
    char szModName[MAX_PATH];
    while (true)
    {
        /* code */
        Sleep(1000);
        for (auto itr = m_running_pid.begin(); itr != m_running_pid.end(); itr++)
        {
            bool found = false;
            if (EnumProcesses(Proc_pid, sizeof(Proc_pid), &Retn_bytes))
            {
                Proc_count = Retn_bytes / sizeof(DWORD);
                SetProcessPrivilege("SeDebugPrivilege", 1);
                for (i = 0; i < Proc_count; i++)
                {
                    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Proc_pid[i]);
                    if (hProcess != NULL)
                    {
                        EnumProcessModules(hProcess, hMod, sizeof(hMod), &Retn_bytes2);
                        GetModuleFileNameEx(hProcess, hMod[0], szModName, sizeof(szModName));
                        // printf("PID=%d Path=%s\n", Proc_pid[i], szModName);
                        if (itr->second == Proc_pid[i])
                        {
                            found = true;
                            break;
                        }
                    }
                    CloseHandle(hProcess);
                }
                SetProcessPrivilege("SeDebugPrivilege", 0);
                if (!found)
                {
                    // 没找到该应用实例就重启应
                    auto it = m_path_process.begin();
                    while (it != m_path_process.end())
                    {
                        if (*it == itr->first)
                        {
                            it = m_path_process.erase(it);
                        }
                        else
                            it++;
                    }
                    for (auto it = m_path_process.begin(); it != m_path_process.end(); it++)
                    {
                        std::cout << *it << std::endl;
                    }
                    this->AddNewProcess(itr->first);
                }
            }
        }
    }
    return 0;
#endif

#ifdef linux
    while (true){
        auto process = RangeProcess();
        usleep(50000);
        for (auto itr = m_running_pid.begin(); itr != m_running_pid.end(); itr++)
        {
            bool found = false;
            for (auto itr2 = process.begin(); itr2 != process.end(); itr2++)
            {
                if (itr->second == itr2->pid)
                {
                    // std::cout << itr->second << itr2->pid << std::endl;
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                // 没找到该应用实例就重启应
                auto it = m_path_process.begin();
                while (it != m_path_process.end())
                {
                    if (*it == itr->first)
                    {
                        it = m_path_process.erase(it);
                    }
                    else
                        it++;
                }
                for (auto it = m_path_process.begin(); it != m_path_process.end(); it++)
                {
                    std::cout << *it << std::endl;
                }
                std::cout << "add new process" << std::endl;
                if (0 > this->AddNewProcess(itr->first))
                    std::cout << "error not found" << std::endl;
            }            
        }
    }
#endif
}

int DaemonizeMonitor::StopMonitor(string)
{
#ifdef _WIN32
    return 0;
#endif
}

int DaemonizeMonitor::StopProcess(string path)
{
    if (path == "")
    {
        return -1;
    }
#ifdef _WIN32
    if (m_running_pid.find(path) != m_running_pid.end())
    {
        DWORD pid = m_running_pid.at(path);
        m_running_pid.erase(path);
        if (KillProcess(pid))
            return 0;
        else
        {
            return -1;
        }
    }
    return 0;
#endif
#ifdef linux
#endif
}

